/*HEADER**********************************************************************
*
* Copyright 2008 Freescale Semiconductor, Inc.
* Copyright 1989-2008 ARC International
*
* This software is owned or controlled by Freescale Semiconductor.
* Use of this software is governed by the Freescale MQX RTOS License
* distributed with this Material.
* See the MQX_RTOS_LICENSE file distributed for more details.
*
* Brief License Summary:
* This software is provided in source form for you to use free of charge,
* but it is not open source software. You are allowed to use this software
* but you cannot redistribute it or derivative works of it in source form.
* The software may be used only in connection with a product containing
* a Freescale microprocessor, microcontroller, or digital signal processor.
* See license agreement file for full license terms including other
* restrictions.
*****************************************************************************
*
* Comments:
*
*   This file is an example of device drivers for the CDC class. This example
*   demonstrates the virtual serial port capability with abstract control model.
*   Redirects the communication from CDC device, which is connected to the board,
*   to the serial port ttyB.
*
*
*END************************************************************************/

#include <mqx.h>
#include <lwevent.h>

#include <bsp.h>

#include <usb.h>
#include <hostapi.h>
#include <usb_host_hub_sm.h>
#include <usb_host_serial.h>

#include "usb_serial.h"

#if ! BSPCFG_ENABLE_IO_SUBSYSTEM
#error This application requires BSPCFG_ENABLE_IO_SUBSYSTEM defined non-zero in user_config.h. Please recompile BSP with this option.
#endif


#if ! BSPCFG_ENABLE_TTYA
//#error This application requires BSPCFG_ENABLE_TTYA defined non-zero in user_config.h. Please recompile BSP with this option.
#endif

#if ! BSPCFG_ENABLE_ITTYB
//#error This application requires BSPCFG_ENABLE_ITTYB defined non-zero in user_config.h. Please recompile BSP with this option.
#endif


/***************************************
**
** Global
*/

/* Table of driver capabilities this application wants to use */
static  USB_HOST_DRIVER_INFO DriverInfoTable[] =
{
   {
      {0x00,0x00},                  /* Vendor ID per USB-IF             */
      {0x00,0x00},                  /* Product ID per manufacturer      */
      0xff,                         /* Class code                       */
      0xff,                         /* Sub-Class code                   */
      0xff,                         /* Protocol                         */
      2,                            /* Reserved                         */
      usb_host_serial_event        /* Application call back function   */
   },
   #if 0
   /* USB 1.1 hub */
   {

      {0x00,0x00},                  /* Vendor ID per USB-IF             */
      {0x00,0x00},                  /* Product ID per manufacturer      */
      USB_CLASS_HUB,                /* Class code                       */
      USB_SUBCLASS_HUB_NONE,        /* Sub-Class code                   */
      USB_PROTOCOL_HUB_ALL,          /* Protocol                         */
      0,                            /* Reserved                         */
      usb_host_hub_device_event     /* Application call back function   */
   },
   #endif
   {
      {0x00,0x00},                  /* All-zero entry terminates        */
      {0x00,0x00},                  /* driver info list.                */
      0,
      0,
      0,
      0,
      NULL
   }
};

char                *device_name = "tty0:";
const USB_SERIAL_INIT   usb_open_param = {
#if SERIAL_USE_HW_FLOW
    USB_UART_NO_BLOCKING | USB_UART_HW_FLOW
#else
    USB_UART_BLOCKING
#endif
};
LWEVENT_STRUCT_PTR               usb_serial_event;
_usb_device_instance_handle      reg_device = 0;

SERIAL_DEVICE_STRUCT   serial_device;
/*
* The following describes the scenario that the usb device is detached but still with power on.
*
* Once fopen() for usb is called, usb_class_cdc_set_acm_ctrl_state() will result in 'producer' of ep1
* in USB_Class_CDC_Send_Data() being increased by 1. So the original fopen() in USB2UART_Task
* and UART2USB_Task will get the 'producer' added by 2. However, there is only one interrupt pipe,
* created in usb_class_cdc_init_ipipe(), which will cause the 'consumer' in USB_Service_Dic_Bulk_In()
* being synced with 'producer'. So as the interrupt pipe finish retrieving the serial status, the producer
* is NOT equal to(one more greater than) consumer.  Next time on a re-attach event, the device will
* refuse to send any data since the 'producer-consumer' pair of ep1 doesn't match any more. Thus, host
* can't get the correct serial status, though ep2 for normal read/write is alive.
*/
static F_USB_INFO_T s_f_usb_info = {NULL, 0};

void Main_Task(uint32_t);
void USB2UART_Task(uint32_t);
void UART2USB_Task(uint32_t);

#define USB_SERIAL_DELIMITER '\r'
#define USB2UART_TASK (11)
#define UART2USB_TASK (12)
TASK_TEMPLATE_STRUCT  MQX_template_list[] =
{
   {            10,     Main_Task, 1000L, 10L,     "Main",  MQX_AUTO_START_TASK },
   { USB2UART_TASK, USB2UART_Task, 2000L, 12L, "USB2UART",                   0L },
   { UART2USB_TASK, UART2USB_Task, 2000L, 12L, "UART2USB",                   0L },
   { 0L,            0L,    0L,  0L,         0L,                   0L }
};

/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : Main_Task
* Returned Value : none
* Comments       :
*     Execution starts here
*
*END*--------------------------------------------------------------------*/
void Main_Task ( uint32_t param )
{ /* Body */
   USB_STATUS       status = USB_OK;
   _usb_host_handle host_handle;
   _task_id         usb2uart_id;
   _task_id         uart2usb_id;
   
   if (NULL == (usb_serial_event = _mem_alloc(sizeof(*usb_serial_event))))
   {
       printf("\nMemory allocation failed");
      _task_block();
   }
   if (USB_OK != _lwevent_create(usb_serial_event, LWEVENT_AUTO_CLEAR))
   {
       printf("\nlwevent create failed");
      _task_block();
   }
   if (MQX_OK != _lwevent_create(&serial_device.serial_event, LWEVENT_AUTO_CLEAR)) {
       printf("\nlwevent create failed");
      _task_block();
   }

   /* _usb_otg_init needs to be done with interrupts disabled */
   _int_disable();

   _int_install_unexpected_isr();
   if (MQX_OK != _usb_host_driver_install(USBCFG_DEFAULT_HOST_CONTROLLER)) {
      printf("\n Driver installation failed");
      _task_block();
   }

   /*
   ** It means that we are going to act like host, so we initialize the
   ** host stack. This call will allow USB system to allocate memory for
   ** data structures, it uses later (e.g pipes etc.).
   */
   status = _usb_host_init (USBCFG_DEFAULT_HOST_CONTROLLER, &host_handle);

   if (status != USB_OK) 
   {
      printf("\nUSB Host Initialization failed. STATUS: %x", status);
      _int_enable();
      _task_block();
   }

   /*
   ** since we are going to act as the host driver, register the driver
   ** information for wanted class/subclass/protocols
   */
   status = _usb_host_driver_info_register (
                                    host_handle,
                                    DriverInfoTable
                                    );
   if (status != USB_OK) {
      printf("\nDriver Registration failed. STATUS: %x", status);
      _int_enable();
      _task_block();
   }
          
   _int_enable();
   
   /* We suppose that the standard output is interrupt driven uart device */
   printf("\n************************************************");
   printf("\n************Huawei Mx509 3G Modem USB Test******");
   printf("\n************************************************\n");
   
   usb2uart_id = _task_create(0, USB2UART_TASK, 0);
   if (usb2uart_id == MQX_NULL_TASK_ID) {
      printf("\nCould not create USB2UART_task\n");
   }

   uart2usb_id = _task_create(0, UART2USB_TASK, 0);
   if (uart2usb_id == MQX_NULL_TASK_ID) {
      printf("\nCould not create UART2USB_task\n");
   }
   
   while (1) {
      _lwevent_wait_ticks(usb_serial_event, USB_SERIAL_DEV_REGISTERED, TRUE, 0);

      printf("USB_SERIAL_DEV_REGISTERED!\n");
     
      if (NULL == (s_f_usb_info.f_usb = fopen(device_name, (void*) &usb_open_param))) {
         printf("\nInternal error occurred");
         break; /* internal error occurred */
      }
      s_f_usb_info.cnt++;

      _lwevent_set(usb_serial_event, USB_SERIAL_FUSB_OPENED_UART2USB | USB_SERIAL_FUSB_OPENED_USB2UART);
      _lwevent_wait_ticks(usb_serial_event, USB_SERIAL_UART2USB_DONE | USB_SERIAL_USB2UART_DONE, TRUE, 0);

      if(s_f_usb_info.cnt) {
         if(IO_OK != fclose(s_f_usb_info.f_usb)) {
            printf("\nInternal error occurred");
            break; /* internal error occurred */
         }
         s_f_usb_info.cnt--;
      }
   }

   _lwevent_destroy(&serial_device.serial_event);
   _task_block();
}

/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : USB2UART_Task
* Returned Value : none
* Comments       :
*     Execution starts here
*
*END*--------------------------------------------------------------------*/
void USB2UART_Task ( uint32_t param )
{ /* Body */
   MQX_FILE_PTR f_uart;
   char buffer[200];
   int num_done, num = 0;
   int i;

   while (1) {
      _lwevent_wait_ticks(usb_serial_event, USB_SERIAL_FUSB_OPENED_USB2UART, TRUE, 0);
      _lwevent_clear(usb_serial_event, USB_SERIAL_DEV_DETACH_USB2UART);
      num_done = num = i = 0;
   
      if (NULL == (f_uart = stdout))
      {
         printf("\nInternal error occurred");
         _task_block(); /* internal error occurred */
      }

      while (1)
      {
        /* read data from USB */
//        printf("start to read usb\n");
        num_done = fread(buffer + num, 1, 64, s_f_usb_info.f_usb); /* read characters from USB */
    
        printf("\n usb recv %d bytes data,send to uart\n", num_done);

        if (num_done > 0)
        {
            num_done = fwrite(buffer, 1, num_done, f_uart);
        }
      }
   }
} /* Endbody */

/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : UART2USB_Task
* Returned Value : none
* Comments       :
*     Execution starts here
*
*END*--------------------------------------------------------------------*/
void UART2USB_Task ( uint32_t param )
{ /* Body */
   MQX_FILE_PTR     f_uart;
   char             buffer[200];
   int              num_done, num = 0;
   unsigned char    i;
   uint32_t         read_done = 0;
   
   while (1){
      _lwevent_wait_ticks(usb_serial_event, USB_SERIAL_FUSB_OPENED_UART2USB, TRUE, 0);
      _lwevent_clear(usb_serial_event, USB_SERIAL_DEV_DETACH_UART2USB);
      num_done = num = i = 0;
   
      if (NULL == (f_uart = stdin)) {
         printf("\nInternal error occurred");
         _task_block(); /* internal error occurred */
      }

      while (1)
      {
        num = 0;
        do
        {
            num_done = fread(buffer + num, 1, 1, f_uart);
            if (IO_ERROR == num_done)
            {
                continue; /* device was detached */
            }
            else
            {
                /* send the string just read */
                if(USB_SERIAL_DELIMITER == *(buffer + num)) 
                {
                    read_done = 1;
                }
                num += num_done;
            }
        }
        while(!read_done);
        
        printf("\n uart recv %d bytes data,send to usb\n", num);
        
        read_done =0;
         /* write them to USB */
         if (num)
         {
            num_done = fwrite(buffer, 1, num, s_f_usb_info.f_usb);
         }
      }
   } /* Endbody */
}


/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : usb_host_cdc_acm_event
* Returned Value : None
* Comments       :
*     Called when acm interface has been attached, detached, etc.
*END*--------------------------------------------------------------------*/

void usb_host_serial_event
   (
      /* [IN] pointer to device instance */
      _usb_device_instance_handle      dev_handle,

      /* [IN] pointer to interface descriptor */
      _usb_interface_descriptor_handle intf_handle,

      /* [IN] code number for event causing callback */
      uint32_t                          event_code
   )
{ /* Body */
   switch (event_code) {
      case USB_CONFIG_EVENT:
         /* Drop through into attach, same processing */
         printf("\n----- USB_CONFIG_EVENT -----\n");
         break;
         
      case USB_ATTACH_EVENT: 
        printf("\n----- USB_ATTACH_EVENT -----\n");
         if (serial_device.DEV_STATE == USB_DEVICE_IDLE) {
            serial_device.DEV_HANDLE    = dev_handle;
            serial_device.INTF_HANDLE   = intf_handle;
            serial_device.DEV_STATE     = USB_DEVICE_ATTACHED;
         } else {
            printf("Serial device already attached - DEV_STATE = %d\n", serial_device.DEV_STATE);
         } /* Endif */
        
         /* initialize new interface members and select this interface */
         if (USB_OK != _usb_hostdev_select_interface(dev_handle,
                                                     intf_handle, 
                                                     (void *)&serial_device.CLASS_INTF))
         {
            break;
         }
         break;
         
      case USB_INTF_EVENT: 
      {
         USB_STATUS              status;
         CLASS_CALL_STRUCT_PTR   serial_parser;
         USB_SERIAL_CLASS_INTF_STRUCT_PTR  if_ptr;

         printf("\n----- USB_INTF_EVENT -----\n");

         serial_parser = &serial_device.CLASS_INTF;
         if (NULL == serial_parser)
         {
             break;
         }

         if (MQX_OK != usb_class_serial_use_lwevent(serial_parser, &serial_device.serial_event))
             break;       
 
         status = usb_class_serial_init_ipipe(serial_parser);

         if ((status != USB_OK) && (status != USBERR_OPEN_PIPE_FAILED))
             break;

         if (USB_OK == usb_class_serial_install_driver(serial_parser, device_name)) 
         {
             {
                 if (reg_device == 0) 
                 {
                     reg_device = dev_handle;
                     _lwevent_set(usb_serial_event, USB_SERIAL_DEV_REGISTERED);
                 }
             }
             printf("\n----- Usb Serial Device installed -----\n");
         }
         printf("----- Usb Serial interface selected -----\n");

         break;
      }

      default:
         break;
   } /* EndSwitch */
} /* Endbody */
/* EOF */
